package JUC.第06章_内存模型之JMM;

import org.junit.Test;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 1.volatile  是Java虚拟机提供的轻量级的同步机制
 *     1.1：保证可见性     :   所有线程有自己的工作内存  操作main内存时 先复制再操作 再写入  通知其他线程
 *     1.2：不保证原子性   :   先复制再操作 再写入 中间有时间差 可能会有别的线程操作过了
 *     1.3：不允许重排列   :   在volatile变量进行写操作时加入store屏障指令   StoreStore  volatile写       StoreLoad
 *                          在volatile变量进行读操作时加入Load屏障指令      volatile读   LoadLoad      LoadStore
 *
 * 2.JMM   java内存模型//一种规范
 *    2.1：可见性        线程结束之前 ，必须刷新到主内存
 *    2.2：原子性        线程加锁之前，必须重新读取主内存到自己的工作内存
 *    2.3：有序性
 *
 * 系统主内存共享变量数据修改被写入的时机是不确定的，多线程并发下很可能出现"脏读"，
 * 所以每个线程都有自己的工作内存，线程自己的工作内存中保存了该线程使用到的变量的内存副本拷贝，
 * 线程对变量的所有操作(读取，赋值等)都必需在线程自己的工作内存中进行，而不能够直接读写主内存中的变量。
 * 不同线程之间也无法直接访问对方工作内存中的变量，线程间变量值的传递均需要通过主内存来完成
 *
 */
public class B1_JMM三大特性 {

    @Test
    public void volatile_look()  {
        MyData myData = new MyData();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t come in");
            try{TimeUnit.SECONDS.sleep(1);}catch (InterruptedException e){ e.printStackTrace();}
            myData.reset();//设置 新的值
            System.out.println(Thread.currentThread().getName() + "\t come to" + myData.number);
        },"A").start();

        while (myData.number == 0){//他会保存以前的0   一直死循环    加volatile就会通知  它失效了
//            try{TimeUnit.MILLISECONDS.sleep(100);}catch (InterruptedException e){ e.printStackTrace();}
//            System.out.print("");//不加volatile  在循环体里加一些东西   也可以让my.number获取新的值  间接保证原子性
//        for   do{}while()  循环同理
            /**
             * while循环体为空为什么不会退出循环，加入一句打印就会退出循环？
             * 加入打印，循环应该也不是正常退出，此时是什么机制退出循环的？
             *
             * 因为多线程之间不是绝对同步的。
             * 在运行时为了提高效率会将数据加载到寄存器中，所以有时虽然内存中数据已经改变，但还未即时更新到寄存器中，就会出现不同步的情况。
             * 至于为什么空执行体迟迟不与内存数据同步，我猜测是访问flag频率过高，jvm的调度机制可能会选择在执行其他任务的时候同步flag，
             * 所以当你加了一句打印语句之后发现flag更新了。所以你每次得到的递增值也是不同的。
             * volatile 关键字可以强制每次都从内存中读取，这样可以实现同步变量，但效率会低不少。
             */
        }
        System.out.println(Thread.currentThread().getName()+"\t over   "+myData.number);
    }  //volatile  保证可见性

    @Test
    public void atomDemo() {
        MyData my =new MyData();

        for (int i = 0; i < 20; i++) {//20000
            new Thread(() ->{
                for (int j = 1; j <= 1000; j++) {
                    my.add();my.atomic();
                }
            },String.valueOf(i)).start();
        }

        while (Thread.activeCount() > 2){ Thread.yield();}//线程个数

        System.out.println(Thread.currentThread().getName() + "\t        int     " + my.number);

        System.out.println(Thread.currentThread().getName() + "\t AtomicInteger  " + my.atomicInteger);
    }        //volatile  不保证原子性


}
class MyData {//MyData.java->MyData.class->jvm字节码
//    int number=0;
    volatile int number=0;
    volatile int number2=0;
    public void reset(){this.number = 60;}
    public  void add(){ ++number;}
    AtomicInteger atomicInteger=new AtomicInteger();//原子性的int
    public  void atomic(){atomicInteger.getAndIncrement();}//i++
    //凭啥能不加锁实现原子性   它使用unsafe（系统原语  不可能被打断） 也用到了CAS（比较并交换）
}
